/** \file    mswin/shared_resource.h
  * \brief   private base for any resource shared between threads that requires synchronization.
  * \date    2003 - 2004
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/include/mswin/shared_resource.h,v 1.6 2004/08/02 10:49:26 cscom1r4 Exp $
  */

#ifndef mswin_shared_resource_h
#define mswin_shared_resource_h

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <assert.h>

namespace mswin {

///this smart object unlocks controlled resource at the end of its lifetime
template <class R, void (R::*claim)(), void (R::*release)()>
class auto_lock {
public:
	auto_lock(R & res, bool initial_lock = true);
	~auto_lock();
	void lock();
	void unlock();
	bool is_locked();
private:
	R & r;
	bool locked;
};

///this type of resources allows exclusive access only from thread at a time
class shared_resource
{
public:
	shared_resource();
	~shared_resource();
public:
	void lock();
	void unlock();
public:
	typedef mswin::auto_lock<shared_resource, &shared_resource::lock, &shared_resource::unlock> 
		auto_lock;
private: //prohibit copy constructors
	shared_resource(const shared_resource &);
	shared_resource & operator = (const shared_resource &);
private:
	CRITICAL_SECTION cs;
};

///this type of resources allows one kind of access (read) from any number of threads or another access (write) from one thread only
class shared_rw_resource
{
public:
	shared_rw_resource();
	void lock_r();
	void unlock_r();
	void lock_w();
	void unlock_w();
public:
	typedef mswin::auto_lock<shared_rw_resource, &shared_rw_resource::lock_r, &shared_rw_resource::unlock_r> 
		auto_rlock;
	typedef mswin::auto_lock<shared_rw_resource, &shared_rw_resource::lock_w, &shared_rw_resource::unlock_w> 
		auto_wlock;
private: //prohibit copy constructors
	shared_rw_resource(const shared_rw_resource &);
	shared_rw_resource & operator = (const shared_rw_resource &);
private:
	shared_resource r;
	shared_resource w;
	volatile LONG readers_count;
};

//inlines

//auto_lock

template <class R, void (R::*claim)(), void (R::*release)()>
inline auto_lock<R, claim, release>::auto_lock(R & res, bool initial_lock) : r(res), locked(false)
	{ if (initial_lock) lock(); }

template <class R, void (R::*claim)(), void (R::*release)()>
inline auto_lock<R, claim, release>::~auto_lock()
	{ if (locked) unlock(); }

template <class R, void (R::*claim)(), void (R::*release)()>
inline void auto_lock<R, claim, release>::lock() {
	(r.*claim)();
	assert(!locked);
	locked = true;
}

template <class R, void (R::*claim)(), void (R::*release)()>
inline void auto_lock<R, claim, release>::unlock() {
	assert(locked);
	locked = false;
	(r.*release)();
}

template <class R, void (R::*claim)(), void (R::*release)()>
inline bool auto_lock<R, claim, release>::is_locked()
	{ return locked; }

//shared_resource

inline shared_resource::shared_resource()
	{ InitializeCriticalSection(&cs); }

inline shared_resource::~shared_resource()
	{ DeleteCriticalSection(&cs); }

inline void shared_resource::lock()
	{ EnterCriticalSection(&cs); }

inline void shared_resource::unlock()
	{ LeaveCriticalSection(&cs); }

//shared_rw_resource

inline shared_rw_resource::shared_rw_resource()
	{ readers_count = 0; }

inline void shared_rw_resource::lock_r() {
	shared_resource::auto_lock l(r);
	if (InterlockedIncrement(&readers_count) == 1)
		w.lock();
}

inline void shared_rw_resource::unlock_r() {
	shared_resource::auto_lock l(r);
	if (InterlockedDecrement(&readers_count) == 0)
		w.unlock();
}

inline void shared_rw_resource::lock_w()
	{ w.lock(); }

inline void shared_rw_resource::unlock_w()
	{ w.unlock(); }

} //namespace mswin

#endif //mswin_shared_resource_h
